// CustomDrawTabCtrl.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "commctrl.h"
#include "CustomDrawTabCtrl.h"
#pragma comment(lib, "comctl32.lib")

#define MAX_LOADSTRING 100
#define TAB_COUNT      3

#define COL_SIDEBAR_BG           RGB(51, 51, 51)
#define COL_SIDEBAR_BG_HOT       RGB(38, 38, 38)
#define COL_SIDEBAR_BORDER       RGB(102, 102, 102)

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
WNDPROC DefWndProcTabControl = NULL;

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
BOOL                CreateTabControl(HWND hwndParent);
LRESULT CALLBACK    TabProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void                PaintTab(HWND hwndTab, HDC hdcTab);
void                DrawTabs(HWND hwndTab, HDC hDCMem, RECT rcTitle);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_CUSTOMDRAWTABCTRL, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CUSTOMDRAWTABCTRL));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CUSTOMDRAWTABCTRL));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_CUSTOMDRAWTABCTRL);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   if (!hWnd)
      return FALSE;

   if (!CreateTabControl(hWnd))
       return FALSE;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

HFONT CreateSimpleFont(HDC hdc, const WCHAR *fontName, int fontSize)
{
    LOGFONT lf = { 0 };

    lf.lfWidth = 0;
    lf.lfHeight = -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
    lf.lfItalic = FALSE;
    lf.lfUnderline = FALSE;
    lf.lfStrikeOut = FALSE;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = OUT_TT_PRECIS;
    lf.lfQuality = DEFAULT_QUALITY;
    lf.lfPitchAndFamily = DEFAULT_PITCH;
    lf.lfWeight = FW_NORMAL;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    wcscpy_s(lf.lfFaceName, _countof(lf.lfFaceName), fontName);

    return CreateFontIndirect(&lf);
}

int InsertTabItem(HWND hTab, LPTSTR pszText, int iid)
{
    TCITEM ti = {0};
    ti.mask = TCIF_TEXT | TCIF_IMAGE;
    ti.pszText = pszText;
    ti.cchTextMax = wcslen(pszText);
    ti.iImage = iid;

    return (int)SendMessage(hTab, TCM_INSERTITEM, iid, (LPARAM)&ti);
}

BOOL CreateTabControl(HWND hwndParent)
{
    // Load and register Tab control class
    INITCOMMONCONTROLSEX iccx;
    iccx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccx.dwICC = ICC_TAB_CLASSES;
    if (!InitCommonControlsEx(&iccx))
        return FALSE;

    // Create the Tab control 
    RECT rc = {10, 10, 310, 510};
    HWND hTab = CreateWindowEx(0, WC_TABCONTROL, 0, 
        TCS_OWNERDRAWFIXED | TCS_FIXEDWIDTH | /*TCS_BOTTOM |*/TCS_HOTTRACK | TCS_VERTICAL |  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 
        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
        hwndParent, (HMENU)0, hInst, 0);

    // Set image list of the tabs
    HIMAGELIST himgl = ImageList_Create(30, 30, ILC_COLOR32, 0, 6); 
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)));
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON2)));
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON3)));
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON4)));
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON5)));
    ImageList_AddIcon(himgl, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON6)));
    TabCtrl_SetImageList(hTab, himgl);

    TabCtrl_SetItemSize(hTab, 44, 41);

    InsertTabItem(hTab, L"Pages", 0);
    InsertTabItem(hTab, L"Bookmarks", 1);
    InsertTabItem(hTab, L"Signatures", 2);
    
    DefWndProcTabControl = (WNDPROC)SetWindowLongPtr(hTab, GWLP_WNDPROC, (LONG_PTR)TabProc);

    return TRUE;
}

LRESULT CALLBACK TabProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{    
    switch (message)
    {
    case WM_PAINT:
        if (GetWindowLongPtr(hWnd, GWL_STYLE) & TCS_OWNERDRAWFIXED)
        {
            // Remove all pending paint requests
            PAINTSTRUCT ps = { 0 };
            HDC hdcTab = BeginPaint(hWnd, &ps);

            // Now call our local Paint
            PaintTab(hWnd, hdcTab);

            EndPaint(hWnd, &ps);

            return 0L;
        }
        break;
    case WM_ERASEBKGND:
        return TRUE;    // Tell system we have processed this message, avoid flicker.
    }

    return CallWindowProc(DefWndProcTabControl, hWnd, message, wParam, lParam);
}

void PaintTab(HWND hwndTab, HDC hdcTab1)
{
    RECT rcClient = { 0 };
    RECT rcPage = { 0 };
    RECT rcPageClient = { 0 };
    GetClientRect(hwndTab, &rcClient);

    rcPage = rcClient;
    TabCtrl_AdjustRect(hwndTab, FALSE, &rcPage);

    rcPageClient.left   = rcPage.left + 4;
    rcPageClient.top    = rcPage.top + 25;
    rcPageClient.right  = rcPage.right - 6;
    rcPageClient.bottom = rcPage.bottom - 5;

    // Create a clipping region.
    HRGN hrgn1 = CreateRectRgnIndirect(&rcClient);
    HRGN hrgn2 = CreateRectRgnIndirect(&rcPage);		
    HRGN hrgn3 = CreateRectRgnIndirect(&rcPageClient);		

    HRGN hrgnClip13 = CreateRectRgn(0, 0, 0, 0);
    CombineRgn(hrgnClip13, hrgn1, hrgn3, RGN_DIFF);

    HRGN hrgnClip23 = CreateRectRgn(0, 0, 0, 0);
    CombineRgn(hrgnClip23, hrgn2, hrgn3, RGN_DIFF);

    // Create memory DC.
    HDC hdcTab = GetDC(hwndTab);
    HDC hdcMem = CreateCompatibleDC(hdcTab);
    HBITMAP hbmp = CreateCompatibleBitmap(hdcTab, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
    DeleteObject(SelectObject(hdcMem, hbmp));

    // Use the region in the memory D
    SelectClipRgn(hdcMem, hrgnClip13);

    HBRUSH hBrush = CreateSolidBrush(COL_SIDEBAR_BG);
    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcMem, hBrush);
    PaintRgn(hdcMem, hrgnClip13);

    hBrush = CreateSolidBrush(COL_SIDEBAR_BG_HOT);
    DeleteObject(SelectObject(hdcMem, hBrush));
    PaintRgn(hdcMem, hrgnClip23);
    DeleteObject(SelectObject(hdcMem, hOldBrush));

    OffsetRect(&rcPage, -1, 0);
    hBrush = CreateSolidBrush(COL_SIDEBAR_BORDER);
    FrameRect(hdcMem, &rcPage, hBrush);

    RECT rcTitle   = { 0 };
    rcTitle.top    = rcPage.top;
    rcTitle.bottom = rcPageClient.top;
    rcTitle.left   = rcPageClient.left;
    rcTitle.right  = rcPageClient.right;

    // Draw the tab buttons on the memory DC
    DrawTabs(hwndTab, hdcMem, rcTitle);

    // Now copy from memory DC to the window DC
    SelectClipRgn(hdcTab, hrgnClip13);
    BitBlt(hdcTab, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY);
    ReleaseDC(NULL, hdcTab);
}

void DrawTabs(HWND hwndTab, HDC hDCMem, RECT rcTitle)
{
    HIMAGELIST hImgList = NULL;
    int xImage = 0;
    int yImage = 0;
    int yOffset = 0;
    int xOffset = 0;

    hImgList = TabCtrl_GetImageList(hwndTab);
    if (!hImgList)
        return;
    if (!ImageList_GetIconSize(hImgList, &xImage, &yImage))
        return;

    TCHITTESTINFO hti = { 0 };
    hti.flags = TCHT_ONITEM;
    GetCursorPos(&hti.pt);
    ScreenToClient(hwndTab, &hti.pt);

    // Draw the tab buttons:
    for (int i = 0; i < TabCtrl_GetItemCount(hwndTab); ++i)
    {
        RECT rcItem = { 0 };
        BOOL bResult = TabCtrl_GetItemRect(hwndTab, i, &rcItem);
        if (bResult & !IsRectEmpty(&rcItem))
        {
            OffsetRect(&rcItem, 2, 2);

            WCHAR wszText[32] = { 0 };
            TCITEM tcItem = { 0 };
            tcItem.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_STATE;
            tcItem.cchTextMax = _countof(wszText);
            tcItem.pszText = wszText;
            TabCtrl_GetItem(hwndTab, i, &tcItem);   

            if (i == TabCtrl_GetCurSel(hwndTab))
            {
                // Draw highlight tab background
                HBRUSH hBrush = CreateSolidBrush(COL_SIDEBAR_BG_HOT);
                FillRect(hDCMem, &rcItem, hBrush);
                DeleteObject(hBrush);

                // Draw highlight tab border
                HPEN hPen = CreatePen(PS_SOLID, 1, COL_SIDEBAR_BORDER);
                HPEN hOldPen = (HPEN)SelectObject(hDCMem, (HGDIOBJ)hPen);
                MoveToEx(hDCMem, rcItem.right-1, rcItem.top, NULL);
                LineTo(hDCMem, rcItem.left, rcItem.top);
                LineTo(hDCMem, rcItem.left, rcItem.bottom);
                LineTo(hDCMem, rcItem.right, rcItem.bottom);
                DeleteObject(SelectObject(hDCMem, hOldPen));

                // Draw highlight tab text
                if (!IsRectEmpty(&rcTitle))
                {
                    HFONT hfnt, hOldFont;
                    hfnt = CreateSimpleFont(hDCMem, L"Microsoft Sans Serif", 8);
                    if (hOldFont = (HFONT)SelectObject(hDCMem, hfnt)) 
                    { 
                        SetBkMode(hDCMem, TRANSPARENT);
                        SetTextColor(hDCMem, RGB(255, 255, 255));

                        SIZE txtSize = { 0 };
                        GetTextExtentPoint32(hDCMem, wszText, wcslen(wszText), &txtSize);
                        int txtX = rcTitle.left;
                        int txtY = (rcTitle.bottom + rcTitle.top - txtSize.cy) / 2;
                        TextOut(hDCMem, txtX, txtY, wszText, wcslen(wszText));
                        SelectObject(hDCMem, hOldFont); 
                    } 
                }

                tcItem.iImage += TAB_COUNT; // The offset ID of TAB_COUNT is the corresponding hot icon.
            }
            // Note that, The tab control WHND will received WM_PAINT message when mouse move, so we can test it here.
            // But why WM_PAINT message was posted?
            else if (i == TabCtrl_HitTest(hwndTab, &hti))
            {
                tcItem.iImage += TAB_COUNT;
            }

            // Draw icon
            xOffset = (rcItem.right - rcItem.left - xImage) / 2;
            yOffset = (rcItem.bottom - rcItem.top - yImage) / 2;
            HICON hIcon = ImageList_GetIcon(hImgList, tcItem.iImage, ILD_NORMAL);
            DrawIconEx(hDCMem, rcItem.left+xOffset, rcItem.top+yOffset, hIcon, 0, 0, 0, NULL, DI_NORMAL|DI_COMPAT);
            DestroyIcon(hIcon);
        }
    }
}
